home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Collections: Various
/
DevDisk 65 (1989)(DevWare PD).zip
/
DevDisk 65 (1989)(DevWare PD).adf
/
3d
/
3d.doc
< prev
next >
Wrap
Text File
|
1990-08-07
|
27KB
|
1,132 lines
3D Library
version 1.0
by Steven Ludtke
January 5, 1990
NOTICE
The 3d library and all associated software in this distribution
is Copyright 1990 by Steven J. Ludtke. You have permission to
use and/or modify this code for any purpose commercial or non-
commercial with two conditions : I must be given credit in any
distributed product's documentation, and if any part of this
package is used in any commercial product (even Shareware) one
free copy of said software must be sent to me at the following
address : Steven Ludtke, 406 Yale Cir., Glenwood Springs, CO
81601; all other royalties are waived. This Copyright notice
must accompany any distributions of any part of this package,
and in general, the package should be distributed intact, with
no modifications. This notice must not be removed from the 3d.c,
test.c, and cube.c source code in this release.
Page 2 - January 5, 1990
Introduction
---------------------
This library represents an attempt to provide the Amiga
community with a high speed, easy to use 3d display library for
C programmers. The library uses the transformation matrix
method, which is the fastest method I know to do 3d
transformations while still providing relatively intuitive
rotations. Integer arithmetic is used for speed. Additionally,
the data is stored in a format that will make it easy to
optimize the code in assembly language. I wrote the code for
Aztec C, but I haven't converted anything to assembly language
yet, so the current version should work with Lattice as well. I
haven't tested it, but I don't believe I did anything that is
compiler dependent.
The library provides what I consider relatively high speed 3d
displays. It can rotate and draw roughly 500 lines/second. Once
I've converted the rotation code to optimized assembly, I expect
the time required for the calculation portion of the display to
be reduced by at least a factor of 4. With the Amiga's graphics
coprocessor, filled polygons can be drawn almost (perhaps 70-
80%) as fast as the lines.
Since a number of the parameters used in the library are
application specific, the 'library' is actually a C source file
that you include in your program. This is done primarily for
speed. If some of the parameters were variables rather than
#defines, the library would run significantly slower. The code
is relatively small, and it seems unlikely that more than one 3d
Page 3 - January 5, 1990
application would be active at one time, so C source seems to me
to be a reasonable way to go. If anyone has a better idea for
future versions, I'd love to hear it.
Included in this package are the following :
3d.c - The actual library code. This file is included in any
program you use the 3d routines in. It must appear in
the correct place in your program. Read the instr. or
look at one of the sample programs for more info.
3d.doc - This file.
makefile - An Aztec makefile for the two sample programs.
cube.c - A sample program that does double buffered animation
of a cube moving around. There is a reason it doesn't
move very smoothly. Look at the code before you think
that's the best the library can do.
test.c - A sample program that allows you to display 3d files
that are in a standard (non-IFF) format. Several
sample files are included. Look at the source for
instructions.
cube.3d - A cube for use with test.c
earth.3d - Outline of the earth's continents for use with
test.c. It should work pretty well with d3surf().
rocket.3d- This is an example of a file that won't work
correctly with 3dsurf().
lay.3d - 4 squares to try with test.c
The two sample programs were written for Aztec C ver 3.6. As
far as I know it will work as is with Lattice, but I could be
wrong. I didn't supply executable for the samples to save a
little space in the distribution.
I have tested all the routines fairly well myself, but this
release is the first time anyone else has had the chance to use
any of this stuff. I should probably call this version a beta-
test release, but I won't. If you have any problems or
suggestions I strongly encourage you to send me a message.
Besides, knowing that someone actually is using this package
would give my ego a little boost.
I can be contacted via any of the following :
stevel@tybalt.caltech.edu (until June 90)
stevel@citiago.bitnet (until June 90)
gluon@theta.caltech.edu
72335,1537 (compuserve)
Steve Ludtke, 1-54 Caltech, Pasadena CA 91126 (until June 90)
Steve Ludtke, 406 Yale Cir, Glen. Spgs. CO 81601 (after that)
My current plans for the next version are as follows :
1. Convert much of the code to assembly for speed.
2. Perhaps add alternative methods for specifying angles, etc...
Page 4 - January 5, 1990
I've heard of some scheme using quaternation (sp?) that's
supposed to be easier for many applications.
3. Perhaps I'll try to add some primitive shading routines
(simple lighting effects).
4. Anything you suggest ...
Please talk to me. I like to get mail. Btw, this code should
be pretty easy to convert for use on other machines. All you
really need to change is the actual graphics function calls.
Here's the actual programming info. Info on structures follows
the function information. I tried to make it as easy to use as
possible.
Before your try to write your own code, you should probably
look at one of the examples. I'll summarize the basic steps
here.
Before you include the 3d.c file, you need to have several
#define statements in you program. If necessary you can replace
some of the defines with global variables, but you should avoid
this as it will slow the program down. The defines you need are
as follows :
#define D3VDIST 5000 <- This is the distance from the
observer to the vanishing point. It
is only used with rotatev().
#define REZ 1024 <- Since the program uses integer
math, it must use several bits
for fractional calculations. 1024
would give 3 digits of accuracy. This
number needs to be a power of 2 for
upward compatibility. In general it
should be at least as large as the
# of pixels in the display you use.
The larger it is, the smaller your
space for points is. REZ=1024 leaves
you with ~4,000,000 possible values
for 'x','y','z'.
Page 5 - January 5, 1990
#define REZB 10 <- This is the number of bits in REZ. It
isn't currently used. It will be in
the next version, so update it.
#define XCEN 250 <- This is the x coord (in the bitmap)
of the center (x=0) coordinate for 3d
displays.
#define YCEN 100 <- Same for y coord.
#define XHI 600 <- This is the highest x value in your
bitmap. Anything above this will be
clipped by the drawing routines.
#define YHI 180 <- Same for y.
#define XLO 0 <- Low x clipping value.
#define YLO 0 <- Low y clipping value.
#define ASPECT 22/10 <- This is the aspect ratio used to
insurea square coord system on the
screen. 22/10 is correct for 640X200
mode. Obviously 11/10 should be used
in 320X200 mode.
#include "3d.c"
Next, you should initialize a VECTOR structure with enough
free memory for your vertex data. Don't forget to allocate
memory for 'tx','ty', and 'tz', too. Allocate memory for a LINES
array as well. Now you can fill in the data in the VECTOR and
LINES structures. Information on this data appears near the end
of the documentation.
Now set up a window or screen (something with a valid
RastPort) or two for double buffering. If you intend to use
d3surf(), call Init3Ras() with one or both RastPorts.
Now SetDrMd() for all RastPorts. If you don't have color data
in your LINES structure, also do a SetAPen() and a SetOPen()
(for surfaces) for all RastPorts.
Now you can actually display data. setxfm() allows you to set
the viewpoint for the drawing. rotatev() or rotate() is used to
actually perform the rotations with or without perspective.
d3surf() and d3lines() can then be used to render the data in a
RastPort.
When you're all done, don't forget to call Exit3d() if you
called Init3Ras(). DON'T call it if you only used d3lines().
Simple huh ??? : )
Page 6 - January 5, 1990
FUNCTION DEFINITIONS :
setxfm - Set rotation and translation values for next xform.
USAGE :
setxfm(a1,a1,a3,x,y,z,m,d)
double a1,a2,a3;
long x,y,z,m,d;
FUNCTION :
This routine sets transformation matrix values based on the
parameters you pass it. The actual matrix is a global array,
however for upward compatibility's sake, you shouldn't access it
except through this routine.
INPUTS :
The three parameters, 'a1-a3' are rotation angles in radians
(see the description of rotations following the structure
definitions). 'x','y','z' are translation parameters. Since
positional values are integral, the translation values must also
be integral.
'n' and 'd' are scaling factors. Since integer arithmetic is
used, a fraction is used for the scaling factor. 'n' is the
numerator and 'd' is the denominator of the scaling factor. The
way this is implemented, it doesn't slow the translations down,
so if you don't need it just set 'n' and 'd' to 1. It will be
just like they didn't exist.
Page 7 - January 5, 1990
OUTPUTS :
None.
---------------------------------------
rotate - Rotates an array of vectors using the current setxfm()
vals without perspective.
USAGE :
rotate(v,n)
VECTOR *v;
int n;
FUNCTION:
This routine actually performs the translation of a group of
points in 3-space based on the parameters set by setxfm().
INPUTS :
'v' points to a vector structure which must be initialized with
pointers to all 6 arrays of size >='n'. The first 'n' vectors
will be rotated. Note that the 'x','y','z' VECTOR values are
unchanged. These values are transformed, and the results are
stored in 'tx','ty','tz'. If you wish to transform a subset of
the points, a separate VECTOR structure pointing to the
beginning of the subset can be used.
OUTPUTS:
none.
---------------------------------------
rotatev - Rotates an array of vectors using the current setxfm
vals with perspective.
USAGE :
rotatev(v,n)
VECTOR *v;
int n;
FUNCTION:
This routine actually performs the translation of a group of
points in 3-space based on the parameters set by setxfm(). The
only difference between this routine and rotate() is the
addition of perspective.
INPUTS :
'v' points to a vector structure which must be initialized with
pointers to all 6 arrays of size >='n'. The first 'n' vectors
will be rotated. Note that the 'x','y','z' VECTOR values are
unchanged. These values are transformed, and the results are
Page 8 - January 5, 1990
stored in 'tx','ty','tz'. If you wish to transform a subset of
the points, a separate VECTOR structure pointing to the
beginning of the subset can be used.
OUTPUTS:
none.
---------------------------------------
Init3Ras - Sets a rastport up for area fills.
USAGE:
Init3Ras(rp,rp2)
struct RastPort *rp,*rp2;
FUNCTION:
This routine should be called once for each RastPort to be used
with d3surf(). If it is called more than once with the same
RastPort it could cause serious problems. This routine only
needs to be called if d3surf() is going to be used. If only
d3lines() is going to be used, you can save some memory by not
calling this routine.
INPUTS:
'rp' and 'rp2' point to RastPorts which will be used with
d3surf(). d3surf uses area fill operations, so the RastPort it
uses must be given temporary storage for use in the filling.
'rp2' should be set to NULL if only one RastPort is to be used.
If both are passed, they will be assigned the same temporary
storage area, thus saving a significant amount of memory.
OUTPUTS:
None.
---------------------------------------
Exit3d - Frees up the memory allocated by Init3Ras.
USAGE :
Exit3d(rp)
struct RastPort *rp;
FUNCTION:
This routine frees up the memory allocated by Init3Ras().
INPUTS:
'rp' points to a RastPort initialized with Init3Ras(). If 2
RastPorts were initialized with a SINGLE call to Init3Ras(),
only ONE of the two should be passed to Exit3d(), and neither
should be used with area fills again.
I should explain this in more detail. If two RastPorts are
Page 9 - January 5, 1990
initialized with a single Init3Ras() call, both RastPorts will
point to the same scratch memory area. Exit3d() frees this
memory up. After you call it for one of the two RastPorts, the
other one will point to an unallocated area in memory. If a fill
operation is done at this time, it can cause all sorts of nasty
problems. Be warned.
OUTPUTS:
None.
---------------------------------------
d3lines - Draw a group of lines connecting points in 3 space.
USAGE:
d3lines(vector,line,n,rp)
VECTOR *vector;
LINES *line;
int n;
struct RastPort *rp;
FUNCTION:
This routine actually draws lines connecting one or more points
in 3-space. See the information on the VECTOR and LINES
structures for more info.
INPUTS:
'vector' points to a VECTOR structure with information on the
points to be used in the line drawing. 'line' points to an array
of LINES structures which contains a description of how the
lines are to be drawn. 'n' is the number of elements in the
array pointed to by 'line'. 'rp' points to the RastPort where
the lines should be drawn.
OUTPUTS:
none.
---------------------------------------
d3surf - Draw a group of surfaces in 3 space.
Page 10 - January 5, 1990
USAGE:
d3lines(vector,line,n,rp)
VECTOR *vector;
LINES *line;
int n;
struct RastPort *rp;
FUNCTION:
This routine is just like d3lines(), except filled polygons are
drawn rather than lines. The polygons are sorted before drawing,
so hidden line removal is accomplished.
INPUTS:
'vector' points to a VECTOR structure with information on the
points to be used in the surface drawing. 'line' points to an
array of LINES structures which contains a description of how
the surfaces are to be drawn. 'n' is the number of elements in
the array pointed to by line. 'rp' points to the RastPort where
the surfaces should be drawn.
OUTPUTS: none.
STRUCTURES :
VECTOR ->
long *x,*y,*z;
long *tx,*ty,*tz;
This structure points to the data for all the vertices that
are transformed. 'x','y','z' are arrays with the untransformed
vertex information. 'tx','ty','tz' are arrays which contain the
transformed equivalents of the vertices. 'tx','ty','tz' are
filled in by rotate() and rotatev(). They must be initialized to
point to sufficiently large areas of free memory by the user.
The 'x','y','z' values represent vertical pixels on the
screen. ie - the point (10,1,10) without perspective would
appear 10 pixels below and 22 pixels to the right of the defined
center (using a 22/10) aspect ratio.
-----------------------------------------
LINES ->
unsigned short l;
unsigned short nl : 1,nc : 1, nco : 1;
For those of you not familiar with the above notation, nl, nc,
and nco are 1 bit integers allocated within a single unsigned
short. An array of LINES structures contains the information for
line/polygon drawing. The fact that 'l' is allocated as a short
means that you can't have more than 2^16 vertices in any single
array. Hopefully this won't present any problems.
Page 11 - January 5, 1990
The array of LINES should be used as follows :
'l' contains a vertex number in a VECTOR structure or a color
(see below).
'nl' is set if this vertex is the beginning of a new
line/polygon.
'nc' is set if 'l' represents a new fill color for polygons.
'nco' is set if 'l' represents a new outline color for polygons,
or a new line color for line drawing.
If filled shapes are to be drawn, not just lines, be sure that
each new line represents a new polygon. That is, don't draw two
filled shapes with one continuous line. This would look fine for
d3lines(), but would produce strange effects with d3surf().
Points in filled shapes need not be in the same plane, but think
carefully before trying this. Some shapes may look strange in
some perspectives.
Color changes with d3lines() may occur anywhere. However,
color changes used with d3surf() must occur only immediately
after a nl=1 element. This is due to the way hidden line removal
works. Every nl=1 element is taken to be the beginning of a new
polygon. These polygons are then sorted so the farthest ones are
drawn first. Color changes occurring before a nl=1 point will be
attached to the wrong polygon when they are sorted. Color
changes in the middle of the shape may not have the desired
effect (after 1 or more lines have been added).
Here is a sample LINES array to make this all more clear :
l nl nc nco
0 0 1 0 0 <- Starts a shape, vertex 0
1 2 0 0 1 <- Changes to outline color 2
2 1 0 1 0 <- Changes to filled color 1
3 1 0 0 0
4 2 0 0 0
5 3 0 0 0
6 0 0 0 0 <- Closes the quad., not necessary
for d3surf().
7 3 0 0 1 <- Changes outline color. This is
fine for d3lines(), but won't
change correctly for d3surf().
8 4 1 0 0 <- New shape
9 5 0 0 0
10 6 0 0 0 <- No color was defined after the
new shape started, so the color
from the last drawn shape will
be used. The shape is not
Page 12 - January 5, 1990
closed. d3surf() will close the
shape anyway. d3lines won't.
In the above example, 0-6 will draw a quadrilateral in color 2
if d3lines() is used. It will draw a filled quad., filled with
color 1 and outlined in color 2, if d3surf() is used.
ROTATIONS, AND HOW THEY'RE DONE
-------------------------------
This program uses a very standard scheme for rotating objects
which is completely general. That is, you can observe your
'scene' from absolutely any imaginable perspective (within the
bounds of integer math).
The axes are set up as follows :
Z| / Y
| /
| /
|/
-------------
X
That is, the X axis is the screen x axis, the Z axis is the
screen y axis, and the Y axis goes positive into the screen.
Rotations are defined in terms of three steps. 'a1' defines a
rotation angle from the X axis in the X-Y plane. 'a2' then
rotates the specified number of radians away from the Z axis in
the Y-Z plane. Finally 'a3' defines a rotation in radians from
the new (rotated) X axis in the new X-Y plane.
Explained another way : Say the ground is in the X-Y plane, ie
going into the screen. 'a1' would spin an object resting on the
ground as if it were a top. 'a2' would cause this rotated object
to tilt on the screen, just like the Aster*ids spaceship spins.
'a3' would take the tilted object and spin it along the tilted
Page 13 - January 5, 1990
plane. a3 is a bit hard to picture, play with it.
I know this is somewhat complex, but you can do any possible
rotation with it once you get the hang of it. It may also be
easier to visualize if you picture the observer moving, rather
than the object being rotated. I may try to add a few other
schemes in the next version. We'll see what I can find.
For those of you who are interested you can see how this
transformation works in "Classical Mechanics" by Goldstein on
page 146. (What can I say, I'm a physics major.)
---------
Again, if you run into any bugs or problems please let me
know. Also keep in mind that, while you may feel free to modify
this code, you might have compatibility problems if you do. I
tried to comment the code as well as possible for those of you
who feel like making changes. Have fun !!!